﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ZeroBugBounce.Missive;
using ZeroBugBounce.Missive.Patterns;

namespace ConsoleTestClient
{
	public class A { }

	public class B : A { }

	class Program
	{
		static void Main(string[] args)
		{
			ManyHandlersWithSingleReply(100, 1000000);
			ManyHandlersWithSingleReplyCancelable(100, 400000);
			ManyHandlersWithSingleReply_SendingLogged(100, 400000);
			ManyHandlersWithSingleReply_HandlingLogged(100, 400000);
			ManyHandlersWithSingleReply_SendingAndHandlingLogged(100, 400000);
			ManyHandlersWithSingleReply_SendingAndHandlingLogged_Cancelable(100, 400000);


			// HandlerTypes();

			#region commented out tests
			//Patterns_AsyncRead();

			//FileWriteHandlerTrial();
			//ManyHandlersWithSingleReplyParallelSend(5, 100000);

			//MsgsPerSecond(1000000, 1000000);
			//ManyHandlersReplyToSameMessage(100, 1000000, 100);
			//ManyHandlersHumanReadable();
			//ManyHandlers(500, 1000000);

			//CancelHandler();

			//ManyHandlersWithSingleReply(500, 1000000);
			////ManyHandlersWithSingleReply_HandlingLogged(500, 1000000);
			////ManyHandlersWithSingleReply_SendingLogged(500, 1000000);
			//ManyHandlersWithSingleReply_SendingAndHandlingLogged(500, 900000);

			//ManyHandlersWithSingleReply(10, 2000);
			//ManyHandlersWithSingleReply(5000, 1500000);
			//ManyHandlersWithSingleReplyNonGeneric(500, 1000000);
			//ManyHandlersWithSingleReplyParallelSend(500, 1000000);

			//ManyHandlersTimeoutPattern(10, 10);
			//	ManyHandlersTimeoutPatternNoTimeouts(500, 20000);

			//ManyHandlersReplyToSameMessage(100, 100000, 100);
			//ManyHandlersReplyToSameMessageSynchronous(100, 100000, 100);
			//ManyHandlersReplyToSameMessage(5000, 1000000, 1);
			//ManyHandlersReplyToSameMessage(5000, 1000000, 10);
			//ManyHandlersReplyToSameMessage(5000, 1000000, 100);
			//ManyHandlersReplyToSameMessage(5000, 1000000, 1000);
			//ManyHandlersReplyToSameMessage(5000, 1000000, 10000);

			//MsgsPerSecond(1000000, 1000000);
			//MsgsPerSecondWithReplies(1000000, 1000000);
			//	Console.ReadLine();
			#endregion
		}

		public static void TheoreticalMaximumSpeed(int iterations)
		{
			Stopwatch timer = new Stopwatch();
			timer.Start();
			int remainingcount = iterations;
			TaskFactory taskFactory = new TaskFactory();
			while (remainingcount > 0)
			{
				var task = taskFactory.StartNew(() => Interlocked.Decrement(ref remainingcount));
			}

			
		}

		public static void MaximumSpeedRunNoReply(int iterations)
		{
			int total = iterations;
			Stopwatch timer = new Stopwatch();
			timer.Start();

			var messenger = new Messenger();
			messenger.Handle(messenger.DefaultTopic,
				(HandlerContext<int> context) => { }, DispatchMode.Asynchronous, false);

			while (iterations >= 0)
			{
				messenger.Send(1, cancelable: false);
				iterations--;
			}

			messenger.Dispatcher.WaitAll();
			timer.Stop();

			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", timer.Elapsed, total * (1000d / ((double)timer.ElapsedMilliseconds)), total);
			Console.WriteLine("Time per message: {0} µs", 1000 * (timer.Elapsed.TotalMilliseconds / ((double)total)));

		}

		public static void TopicHierarchy()
		{
			// top handler gets anything sent to top.*
			// top.down handler gets only things sent to top.down or lower

			int topic1Count = 0;
			int topic2Count = 0;
			string topic1 = "top";
			string topic2 = "top.down";

			var messenger = new Messenger();
			messenger.Handle(topic1, (int i) =>
			{
				Interlocked.Add(ref topic1Count, i);
			});

			messenger.Handle(topic2, (int i) =>
			{
				Interlocked.Add(ref topic2Count, i);
			});

			messenger.Send(topic1, 1);
			messenger.Dispatcher.WaitAll();

			messenger.Send(topic2, 5);
			messenger.Dispatcher.WaitAll();

			Console.WriteLine(topic1Count);
			Console.WriteLine(topic2Count);
		}

		//public static void HandlerTypes()
		//{
		//    var messenger = new Messenger();
		//    messenger.AddHandlerType(typeof(TupleHandler<>));

		//    messenger.Send(new Tuple<int>(123));

		//    messenger.Dispatcher.WaitAll();
		//}

		public class TupleHandler<T> : Handler<Tuple<T>>
		{
			public int HandledCount;
			public override void Handle(HandlerContext<Tuple<T>> context)
			{
				Interlocked.Increment(ref HandledCount);
				//Console.WriteLine("type: {0}   value: {1}", HandledType, context.Message);
			}
		}

		public static void Patterns_AsyncRead()
		{
			var tempFile = Path.GetTempFileName();
			var rng = new Random();
			byte[] buffer = new byte[4096 * 10 + 1];

			rng.NextBytes(buffer);

			File.WriteAllBytes(tempFile, buffer);

			var messenger = new Messenger();
			messenger.AddHandler(new AsyncFileReadHandler(TimeSpan.FromMilliseconds(50)));

			messenger.Send(new AsyncFileReadRequest(tempFile),
				(byte[] fileBytes) =>
				{
					Console.WriteLine(fileBytes.Length);
				});

			Thread.Sleep(Timeout.Infinite);
		}

		public static void ManyHandlersWithSingleReply_SendingAndHandlingLogged_Cancelable(int handlers, int messages)
		{
			int total = 0;
			int sendLogCount = 0;
			int handlerLogCount = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();
			messenger.MessageSent += (s, e) => Interlocked.Increment(ref sendLogCount);
			messenger.MessageHandled += (s, e) => Interlocked.Increment(ref handlerLogCount);
			var rng = new Random();
			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (HandlerContext<int> i) => i.Message += rng.Next(), DispatchMode.Asynchronous, true);
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next(),
					(int reply) => Interlocked.Increment(ref total), true);
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Sends Logged: {0}", sendLogCount);
			Console.WriteLine("Handlers Logged: {0}", handlerLogCount);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		public static void ManyHandlersWithSingleReply_SendingAndHandlingLogged(int handlers, int messages)
		{
			int total = 0;
			int sendLogCount = 0;
			int handlerLogCount = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();
			messenger.MessageSent += (s, e) => Interlocked.Increment(ref sendLogCount);
			messenger.MessageHandled += (s, e) => Interlocked.Increment(ref handlerLogCount);
			var rng = new Random();
			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (int i) => i += rng.Next(), DispatchMode.Asynchronous);
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next(),
					(int reply) => Interlocked.Increment(ref total), false);
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Sends Logged: {0}", sendLogCount);
			Console.WriteLine("Handlers Logged: {0}", handlerLogCount);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		public static void ManyHandlersWithSingleReply_SendingLogged(int handlers, int messages)
		{
			int total = 0;
			int sendLogCount = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();
			messenger.MessageSent += (s, e) => Interlocked.Increment(ref sendLogCount);

			var rng = new Random();
			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (int i) => i += rng.Next(), DispatchMode.Asynchronous);
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next(),
					(int reply) => Interlocked.Increment(ref total), false);
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Sends Logged: {0}", sendLogCount);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		public static void ManyHandlersWithSingleReply_HandlingLogged(int handlers, int messages)
		{
			int total = 0;
			int handlerLogCount = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();
			messenger.MessageHandled += (s, e) => Interlocked.Increment(ref handlerLogCount);

			var rng = new Random();
			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (int i) => i += rng.Next(), DispatchMode.Asynchronous);
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next(),
					(int reply) => Interlocked.Increment(ref total), false);
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Handlers Logged: {0}", handlerLogCount);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		static void CancelHandler()
		{
			Stopwatch timer = new Stopwatch();
			timer.Start();

			var messenger = new Messenger();
			messenger.Handle<string>((msg, xl) =>
			{
				Console.WriteLine(msg);
				for (int count = 0; count < 100000000; count++)
				{
					if (xl.IsCancellationRequested)
						return;

					Thread.Sleep(1);
				}
			});

			var receipt = messenger.Send("Cancel Handler");
			Thread.Sleep(100);

			receipt.Cancel();
			messenger.Dispatcher.WaitAll();

			timer.Stop();
			Console.WriteLine("Was cancelled in {0}ms", timer.ElapsedMilliseconds);
		}

		static void FileWriteHandlerTrial()
		{
			var messenger = new Messenger();
			messenger.AddHandler(new AsyncFileWriteHandler<string>(msg =>
				string.Format("File {0} was written!", msg.Path)));

			var rng = new Random();
			byte[] buffer = new byte[40000000];

			rng.NextBytes(buffer);

			var path = Path.GetTempFileName();
			Console.WriteLine(path);

			var request = new AsyncFileWriteRequest(path, FileMode.Create, buffer);

			var waitHandle = new ManualResetEvent(false);

			Stopwatch timer = new Stopwatch();
			timer.Start();
			messenger.Send(request, (string message) =>
			{
				waitHandle.Set();
				timer.Stop();
				Console.WriteLine(message);
			});

			waitHandle.WaitOne();
			timer.Stop();

			Console.WriteLine("Took {0}ms", timer.ElapsedMilliseconds);
		}

		public static void ManyHandlersWithSingleReplyParallelSend(int handlers, int messages)
		{
			int total = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();

			var rng = new Random();
			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (int i) => i += rng.Next(), DispatchMode.Asynchronous);
			}

			sendTimer.Start();

			Parallel.For(0, messages, new ParallelOptions { MaxDegreeOfParallelism = 2 }, mcount =>
			{
				messenger.Send(topics[mcount % handlers], rng.Next(), (int reply) => Interlocked.Increment(ref total));
			});

			messenger.Dispatcher.WaitAll();
			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		public class ManyHandlersWithSingleReplyNonGenericHandlerHandler : Handler<int>
		{
			public Random rng;
			public override void Handle(HandlerContext<int> context)
			{
				if (context is ReplyHandlerContext<int, int>)
				{
					var replyContext = (ReplyHandlerContext<int, int>)context;
					replyContext.ReplyCallback(rng.Next());
				}
			}
		}

		//public static void MessagePerSecWithHandlerTypes_MultiTypes(int iterations)
		//{
		//    int messages = iterations * 5;
		//    Stopwatch allTimer = new Stopwatch();
		//    Stopwatch sendTimer = new Stopwatch();

		//    allTimer.Start();
		//    var messenger = new Messenger();
		//    messenger.AddHandlerType(typeof(TupleHandler<>));

		//    var rng = new Random();

		//    sendTimer.Start();
		//    for (int index = 0; index < iterations; index++)
		//    {
		//        messenger.Send(new Tuple<int>(rng.Next()));
		//        messenger.Send(new Tuple<uint>((uint)rng.Next()));
		//        messenger.Send(new Tuple<long>(rng.Next()));
		//        messenger.Send(new Tuple<decimal>((decimal)rng.Next()));
		//        messenger.Send(new Tuple<double>(rng.Next()));
		//    }

		//    messenger.Dispatcher.WaitAll();

		//    allTimer.Stop();
		//    sendTimer.Stop();

		//    Console.WriteLine("Count: {0}", messenger.AllHandlers().OfType<TupleHandler<int>>().First().HandledCount);
		//    Console.WriteLine("Count: {0}", messenger.AllHandlers().OfType<TupleHandler<uint>>().First().HandledCount);
		//    Console.WriteLine("Count: {0}", messenger.AllHandlers().OfType<TupleHandler<long>>().First().HandledCount);
		//    Console.WriteLine("Count: {0}", messenger.AllHandlers().OfType<TupleHandler<decimal>>().First().HandledCount);
		//    Console.WriteLine("Count: {0}", messenger.AllHandlers().OfType<TupleHandler<double>>().First().HandledCount);

		//    Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
		//    Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		//}

		//public static void MessagePerSecWithHandlerTypes(int iterations)
		//{
		//    int messages = iterations;
		//    Stopwatch allTimer = new Stopwatch();
		//    Stopwatch sendTimer = new Stopwatch();

		//    allTimer.Start();
		//    var messenger = new Messenger();
		//    messenger.AddHandlerType(typeof(TupleHandler<>));

		//    var rng = new Random();

		//    sendTimer.Start();
		//    for (int index = 0; index < iterations; index++)
		//    {
		//        messenger.Send(new Tuple<int>(rng.Next()));
		//    }

		//    messenger.Dispatcher.WaitAll();

		//    allTimer.Stop();
		//    sendTimer.Stop();

		//    Console.WriteLine("Count: {0}", (messenger.AllHandlers().First() as TupleHandler<int>).HandledCount);
		//    Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
		//    Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		//}

		//public static void ManyHandlersTimeoutPatternNoTimeouts(int handlers, int messages)
		//{
		//    int total = 0;
		//    Stopwatch allTimer = new Stopwatch();
		//    Stopwatch sendTimer = new Stopwatch();
		//    var messenger = new Messenger();

		//    var rng = new Random();
		//    var topics = new List<string>();

		//    allTimer.Start();
		//    for (int hcount = 0; hcount < handlers; hcount++)
		//    {
		//        var topic = Guid.NewGuid().ToString();
		//        if (hcount < messages)
		//        {
		//            topics.Add(topic);
		//        }

		//        messenger.AddHandler(new TimeoutHandler<int>(
		//            TimeSpan.FromMilliseconds(100),
		//            context =>
		//            {
		//                Thread.Sleep(25);
		//            },
		//            context => Interlocked.Increment(ref total)) { Topic = Topic.Parse(topic) });
		//    }

		//    sendTimer.Start();
		//    for (int mcount = 0; mcount < messages; mcount++)
		//    {
		//        messenger.Send(topics[mcount % handlers], rng.Next());
		//    }

		//    messenger.Dispatcher.WaitAll();

		//    sendTimer.Stop();
		//    allTimer.Stop();

		//    Console.WriteLine("Messages: {0}", messages);
		//    Console.WriteLine("Total: {0}", total);
		//    Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
		//    Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
		//    Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		//}

		//public static void ManyHandlersTimeoutPattern(int handlers, int messages)
		//{
		//    int total = 0;
		//    Stopwatch allTimer = new Stopwatch();
		//    Stopwatch sendTimer = new Stopwatch();
		//    var messenger = new Messenger();

		//    var rng = new Random();
		//    var topics = new List<string>();

		//    allTimer.Start();
		//    for (int hcount = 0; hcount < handlers; hcount++)
		//    {
		//        var topic = Guid.NewGuid().ToString();
		//        if (hcount < messages)
		//        {
		//            topics.Add(topic);
		//        }

		//        messenger.AddHandler(new TimeoutHandler<int>(
		//            TimeSpan.FromMilliseconds(10),
		//            context =>
		//            {
		//                Thread.Sleep(3000);
		//            },
		//            context => Interlocked.Increment(ref total)) { Topic = Topic.Parse(topic) });
		//    }

		//    sendTimer.Start();
		//    for (int mcount = 0; mcount < messages; mcount++)
		//    {
		//        messenger.Send(topics[mcount % handlers], rng.Next());
		//    }

		//    messenger.Dispatcher.WaitAll();

		//    sendTimer.Stop();
		//    allTimer.Stop();

		//    Console.WriteLine("Messages: {0}", messages);
		//    Console.WriteLine("Total: {0}", total);
		//    Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
		//    Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
		//    Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		//}

		public static void ManyHandlersWithSingleReplyNonGeneric(int handlers, int messages)
		{
			int total = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();
			var rng = new Random();

			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.AddHandler<int>(new ManyHandlersWithSingleReplyNonGenericHandlerHandler 
				{ rng = rng, Topic = messenger.ParseTopic(topic) });
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next(), (int reply) => Interlocked.Increment(ref total));
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		public class AddRandomIntegerHandler : Handler<int>
		{
			Random rng;			

			public AddRandomIntegerHandler(Topic topic, Random rng)
			{
				this.Topic = topic;
				this.rng = rng;
				this.DispatchMode = ZeroBugBounce.Missive.DispatchMode.Asynchronous;
			}

			public override void Handle(HandlerContext<int> context)
			{
				var answer = rng.Next() + context.Message;
			}
		}

		public static void ManyHandlersWithSingleReplyCancelable(int handlers, int messages)
		{
			int total = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();

			var rng = new Random();
			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (int i) => i += rng.Next(), DispatchMode.Asynchronous);
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next(),
					(int reply) => Interlocked.Increment(ref total), true);
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		public static void ManyHandlersWithSingleReply(int handlers, int messages)
		{
			int total = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();

			var rng = new Random();
			var topics = new List<string>();

			allTimer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (int i) => i += rng.Next(), DispatchMode.Asynchronous);
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next(),
					(int reply) => Interlocked.Increment(ref total), false);
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
			Console.WriteLine("Time per message: {0} µs", 1000 * (sendTimer.Elapsed.TotalMilliseconds / ((double)messages)));
		}

		public static void ManyHandlersReplyToSameMessageSynchronous(int handlers, int messages, int uniqueTopics)
		{
			int total = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();

			var rng = new Random();
			var topics = new List<string>(uniqueTopics);

			allTimer.Start();
			for (int tcount = 0; tcount < uniqueTopics; tcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (tcount < messages)
				{
					topics.Add(topic);
				}
			}

			for (int hcount = 0; hcount < uniqueTopics; hcount++)
			{
				var topic = topics[hcount % uniqueTopics];
				messenger.Handle(topic, (int i) => rng.Next(), DispatchMode.Synchronous);
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % uniqueTopics], rng.Next(), (int reply) => Interlocked.Increment(ref total));
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
		}

		public static void ManyHandlersReplyToSameMessage(int handlers, int messages, int uniqueTopics)
		{
			int total = 0;
			Stopwatch allTimer = new Stopwatch();
			Stopwatch sendTimer = new Stopwatch();
			var messenger = new Messenger();

			var rng = new Random();
			var topics = new List<string>(uniqueTopics);

			allTimer.Start();
			for (int tcount = 0; tcount < uniqueTopics; tcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (tcount < messages)
				{
					topics.Add(topic);
				}
			}

			for (int hcount = 0; hcount < uniqueTopics; hcount++)
			{
				var topic = topics[hcount % uniqueTopics];
				messenger.Handle<int>(topic, i => rng.Next());
			}

			sendTimer.Start();
			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % uniqueTopics], rng.Next(), (int reply) => Interlocked.Increment(ref total));
			}

			messenger.Dispatcher.WaitAll();

			sendTimer.Stop();
			allTimer.Stop();

			Console.WriteLine("Messages: {0}", messages);
			Console.WriteLine("Total: {0}", total);
			Console.WriteLine("Handlers + Message: {2} handlers, {3} messages took {0} or {1} m/s", allTimer.Elapsed, messages * (1000d / ((double)allTimer.ElapsedMilliseconds)), handlers, messages);
			Console.WriteLine("Message Send Only: {2} messages took {0} or {1} m/s", sendTimer.Elapsed, messages * (1000d / ((double)sendTimer.ElapsedMilliseconds)), messages);
		}

		public static void ManyHandlersHumanReadable()
		{
			int messages = 16;

			Stopwatch timer = new Stopwatch();
			var messenger = new Messenger();

			var topics = new List<string>();

			timer.Start();
			topics.Add("Howard");
			topics.Add("Abi");
			topics.Add("Grayson");
			topics.Add("Bernie");

			messenger.Handle("Howard", (int m) => m += 1);
			messenger.Handle("Abi", (int m) => m += 2);
			messenger.Handle("Grayson", (int m) => m += 3);
			messenger.Handle("Bernie", (int m) => m += 4);

			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % 4], 100);
			}

			messenger.Dispatcher.WaitAll();
			timer.Stop();

			Console.WriteLine("{2} handlers, {3} messages took {0} or {1} m/s", timer.Elapsed, messages * (1000d / ((double)timer.ElapsedMilliseconds)), 4, messages);
		}

		public static void ManyHandlers(int handlers, int messages)
		{
			Stopwatch timer = new Stopwatch();
			var messenger = new Messenger();

			var rng = new Random();
			var topics = new List<string>();

			timer.Start();
			for (int hcount = 0; hcount < handlers; hcount++)
			{
				var topic = Guid.NewGuid().ToString();
				if (hcount < messages)
				{
					topics.Add(topic);
				}

				messenger.Handle(topic, (int i) => i += rng.Next());
			}

			for (int mcount = 0; mcount < messages; mcount++)
			{
				messenger.Send(topics[mcount % handlers], rng.Next());
			}

			messenger.Dispatcher.WaitAll();
			timer.Stop();

			Console.WriteLine("{2} handlers, {3} messages took {0} or {1} m/s", timer.Elapsed, messages * (1000d / ((double)timer.ElapsedMilliseconds)), handlers, messages);
		}

		public static void Reply_handlers_works()
		{
			var messenger = new Messenger();
			messenger.Handle("Test", (int x) => x + 13, DispatchMode.Synchronous);
			int r = 0;

			messenger.Send("Test", 5, (int i) => r = i);

			Console.WriteLine("because it ran synchronously, I can report r = {0}", r);
			//Assert.Equal(18, r);
		}

		//static volatile int x = 0;
		//static void CountToAMillion()
		//{
		//    var messenger = new Messenger();
		//    messenger.RegisterHandler(i =>
		//    {
		//        if(i is int)
		//        {
		//            x = (int)i;
		//        }
		//    });

		//    for (int i = 1; i <= 1000000; i++)
		//    {
		//        messenger.Send(i);
		//    }

		//    messenger.WaitAll();
		//    Console.WriteLine(x);
		//}

		public static Double IncrementBy(ref Double target, Double value)
		{
			Double currentVal = target, startVal, desiredVal;

			// Don't access target in the loop except in an attempt  
			// to change it because another thread may be touching it  
			do
			{
				// Record this iteration's starting value 
				startVal = currentVal;

				// Calculate the desired value in terms of startVal and value 
				desiredVal = startVal + value;

				// NOTE: the thread could be preempted here! 

				// if (target == startVal) target = desiredVal 
				// Value prior to potential change is returned 
				currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);

				// If the starting value changed during this iteration, repeat  
			} while (startVal != currentVal);

			// Return the maximum value when this thread tried to set it 
			return desiredVal;
		}

		static void MsgsPerSecond(int iterations)
		{
			//CheckValue = 0;
			var messenger = new Messenger();

			Stopwatch timer = new Stopwatch();

			double actualCalc = 0;
			double checkCalc = 0;

			for (int i = 1; i <= iterations; i++)
			{
				checkCalc += (double)i;
			}

			Console.WriteLine("CheckValue should be: {0}", checkCalc);

			timer.Start();
			messenger.Handle<double>(d =>
			{
				IncrementBy(ref actualCalc, d);
			}, DispatchMode.Asynchronous);

			int countOfMessagesSent = 0;

			for (int i = 1; i <= iterations; i++)
			{
				countOfMessagesSent++;
				messenger.Send((double)i, false);
			}

			messenger.Dispatcher.WaitAll();

			timer.Stop();

			Console.WriteLine("{0} messages sent", countOfMessagesSent);
			Console.WriteLine("Tasks completed.");
			Console.WriteLine("Check Value is {0}", actualCalc);
			Console.WriteLine("Processed {0} messages in {1} sec or {2} m/s", iterations, timer.Elapsed.TotalSeconds, ((double)iterations) / timer.Elapsed.TotalSeconds);
		}

		static void MsgsPerSecondWithReplies(int interval, int iterations)
		{
			var messenger = new Messenger();

			Stopwatch timer = new Stopwatch();

			double actualCalc = 0;

			int countOfRepliesReceived = 0;
			messenger.Handle<double, double>("Test", d =>
			{
				return d + 1d;
			}, DispatchMode.Asynchronous);

			var task = new Task(() =>
			{
				int countOfMessagesSent = 0;

				for (int i = 1; i <= iterations; i++)
				{
					countOfMessagesSent++;
					messenger.Send("Test", (double)i, (double r) =>
					{
						Interlocked.Increment(ref countOfRepliesReceived);
					});
				}

				Console.WriteLine("{0} messages sent", countOfMessagesSent);
			});

			timer.Start();
			task.Start();
			task.Wait();

			messenger.Dispatcher.WaitAll();

			timer.Stop();

			Console.WriteLine("Tasks completed.");
			Console.WriteLine("Check Value is {0}", actualCalc);
			Console.WriteLine("Processed {0} messages with {3} replies in {1} sec or {2} m/s",
				iterations, timer.Elapsed.TotalSeconds, ((double)iterations) / timer.Elapsed.TotalSeconds,
				countOfRepliesReceived);
		}

		static void NoReplyMessages()
		{
			//var messenger = new MessagingCore();
			//messenger.RegisterHandler((int msg) =>
			//{ 
			//    Console.WriteLine("Number sent was {0}", msg);
			//});

			//messenger.RegisterHandler((int msg) =>
			//{
			//    Console.WriteLine("Adding 10 to {0} = {1} and replying", msg, msg + 10);
			//    return msg + 10;
			//});

			//for (int no = 100; no < 115; no += 3)
			//{
			//    messenger.Send(no);
			//}
		}

		static readonly object CheckValueLock = new object();
		static readonly object HandlerLock = new object();

		static double _checkValue = 0;
		//static double CheckValue
		//{
		//    get
		//    {
		//        lock (CheckValueLock)
		//        {
		//            return _checkValue;
		//        }
		//    }
		//    set
		//    {
		//        lock (CheckValueLock)
		//        {
		//            _checkValue = value;
		//        }
		//    }
		//}

		static void Test_locking_effect_on_performance()
		{
			Stopwatch timer = new Stopwatch();

			timer.Start();
			var messenger = new Messenger();
			var waitHandle1 = new ManualResetEvent(false);
			var waitHandle2 = new ManualResetEvent(false);
			var waitHandle3 = new ManualResetEvent(false);

			bool hasExceptions = false;

			AppDomain.CurrentDomain.UnhandledException += (s, e) =>
			{
				Debug.WriteLine(e);
				hasExceptions = true;
			};

			ThreadPool.QueueUserWorkItem(context =>
			{
				try
				{
					for (int iter = 0; iter < 500; iter++)
					{
						messenger.Handle((int x) =>
						{
							var x2 = x - 9299;
						});
					}
				}
				catch (Exception ex)
				{
					hasExceptions = true;
					Debug.WriteLine(ex);
				}
				finally
				{
					waitHandle1.Set();
				}
			});

			ThreadPool.QueueUserWorkItem(context =>
			{
				try
				{
					for (int iter = 0; iter < 500; iter++)
					{
						messenger.Handle((int x) =>
						{
							var x2 = x * 20;
						});
					}
				}
				catch (Exception ex)
				{
					hasExceptions = true;
					Debug.WriteLine(ex);
				}
				finally
				{
					waitHandle2.Set();
				}
			});


			ThreadPool.QueueUserWorkItem(context =>
			{
				try
				{
					Random rng = new Random();
					for (int iter = 0; iter < 500; iter++)
					{
						messenger.Send(rng.Next());
					}
				}
				catch (Exception ex)
				{
					hasExceptions = true;
					Debug.WriteLine(ex);
				}
				finally
				{
					waitHandle3.Set();
				}
			});

			waitHandle1.WaitOne();
			waitHandle2.WaitOne();
			waitHandle3.WaitOne();

			messenger.Dispatcher.WaitAll();

			timer.Stop();

			Console.WriteLine("Total time elapsed with lots of locking: {0}", timer.Elapsed);
		}


		static void MsgsPerSecond2(int interval, int iterations)
		{
			//CheckValue = 0;
			var messenger = new Messenger();
			Stopwatch timer = new Stopwatch();
			int count = 0;

			double checkCalc = 0;

			for (int i = 1; i <= iterations; i++)
			{
				checkCalc += (double)i;
			}

			Console.WriteLine("CheckValue should be: {0}", checkCalc);

			messenger.Handle<double>(d =>
			{
				lock (HandlerLock)
				{
					count++;

					//Console.WriteLine("Adding {0} to {1}", d, CheckValue);
					//CheckValue += d;

					if (count % interval == 0)
					{
						Console.WriteLine("{0:n} messages, {1:0.0}sec	{2:n} m/s	{3:n} m/m	{4:0.000000} sec per msg",
							count,
							timer.Elapsed.TotalSeconds,
							((double)count) / timer.Elapsed.TotalSeconds,
							((double)count) / timer.Elapsed.TotalMinutes,
							timer.Elapsed.TotalSeconds / ((double)count));
					}
				}
			});

			var task = new Task(() =>
			{
				int countOfMessagesSent = 0;

				for (int i = 1; i <= iterations; i++)
				{
					countOfMessagesSent++;
					messenger.Send((double)i);
				}

				Console.WriteLine("{0} messages sent", countOfMessagesSent);
			});

			timer.Start();
			task.Start();
			task.Wait();

			messenger.Dispatcher.WaitAll();
			Thread.Sleep(1000);
			messenger.Dispatcher.WaitAll();
			Thread.Sleep(1000);
			messenger.Dispatcher.WaitAll();

			Console.WriteLine("Tasks completed.");
			//Console.WriteLine("Check Value is {0}", CheckValue);
		}

		static void WithGenericsSimple()
		{
			//var messenger = new MessagingCore();

			//messenger.RegisterHandler((double dbl) => dbl + 1d);
			//var replies = messenger.Send<double, double>(3d).Receive();

			//Console.WriteLine(replies);

			////replies.ToList().ForEach(d => Console.WriteLine(d));
		}

		static void TheSimplestExample()
		{
			//var messenger = new MessagingCore();
			//messenger.Dispatcher = new TaskParallelDispatcher();
			//messenger.RegisterHandler<int>(c =>
			//{
			//    return c + 5;
			//});

			//for (int i = 0; i < 100; i += 10)
			//{
			//    var r = messenger.Send(i);
			//    var result = r.Receive();
			//    Console.WriteLine(result);
			//}
		}

		//static void SimpleMultiReplies()
		//{
		//    MessagingCore messenger = new MessagingCore();
		//    messenger.RegisterHandler<int>(c =>
		//        {
		//            int msg = (int)c.Message;
		//            c.Reply(msg + 10);
		//            c.Reply(msg + 100);
		//            c.Reply(msg + 1000);
		//        });

		//    var replies = messenger.Send(8).ReceiveAll();

		//    foreach (var reply in replies)
		//    {
		//        Console.WriteLine(reply);
		//    }
		//}
	}
}
